﻿using MIO.Client.Protocal;
using MIO.Client.Util;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static MIO.Client.Event.EventManager;

namespace MIO.Client.Event
{
    class EventEntry
    {
        private ConcurrentBag<Delegate> eventBag = new ConcurrentBag<Delegate>();

        public void addEventDelegate<T>(EventPacketDelegate<T> eventPacketDelegate)
        {
            eventBag.Add(eventPacketDelegate);
        }

        public ConcurrentBag<Delegate> getBag()
        {
            return eventBag;
        }


        public void clear()
        {
            eventBag = new ConcurrentBag<Delegate>();
        } 
    }


    public delegate void EventPacketDelegate<T>(IOClient client, string fromSessionId, T data); 
    public delegate void LostPacketDelegate(IOClient client, Packet packet);

    public class EventManager
    {

        private readonly string TAG = nameof(EventManager);
        private IOClient client;
         
        private LostPacketDelegate lostPacketDelegate = null;
        private ConcurrentDictionary<string, EventEntry> allEventEntries = new ConcurrentDictionary<string, EventEntry>();


        public EventManager(IOClient client)
        {
            this.client = client;
        }
         
         

        public void addLostPacketDelegate(LostPacketDelegate lostPacketDelegate)
        {
            this.lostPacketDelegate = lostPacketDelegate;
        }

        public void onLostPacket(IOClient client,Packet packet)
        {
            Task.Factory.StartNew(() => {
                lostPacketDelegate?.Invoke(client, packet);
            });
            
        }

        public void addEventDelegate<T>(string eventName,EventPacketDelegate<T> eventPacketDelegate)
        { 
            if (!this.allEventEntries.ContainsKey(eventName))
            {
                EventEntry eventEntry = new EventEntry();
                allEventEntries.TryAdd(eventName, eventEntry);
            }
            EventEntry g = allEventEntries[eventName];
            g.addEventDelegate(eventPacketDelegate);
        }

        public void removeEventDelegate(string eventName)
        { 
            EventEntry g = allEventEntries[eventName];
            g.clear();
        }
         
        public void onEvent(IOClient client, String eventName, string fromSessionId  ,Object data)
        { 
            if (string.IsNullOrEmpty(eventName))
                return;
            if (data == null)
                return;
            if (allEventEntries.ContainsKey(eventName))
            {
                EventEntry eventEntry = allEventEntries[eventName];
                ConcurrentBag<Delegate> eeBag = eventEntry.getBag();

                 

                foreach (var eventDelegate in eeBag)
                {

                    if(data is string)
                    {
                        object dataFromJson = getEventData((data as string), eventDelegate);
                        eventDelegate.DynamicInvoke(client, fromSessionId, dataFromJson);
                    }
                    else
                    {
                        Type type = getEventListenerClazz(eventDelegate);
                        if (data != null && type == data.GetType())
                        {
                            Task.Factory.StartNew(()=>{
                                eventDelegate.DynamicInvoke(client, fromSessionId, data);
                            });
                        }
                    }
                }
            }
        }

        /**
         * 获取事件值 json 转obj
         *
         * @param jsonString
         * @param dataListener
         * @return
         */
        private Object getEventData(String jsonString, Delegate eventPacketDelegate)
        {
            if (string.IsNullOrEmpty(jsonString))
                return new VoidData();
            Type type = getEventListenerClazz(eventPacketDelegate);
            return JsonUtil.jsonString2Object(jsonString, type);
        }

        private Type getEventListenerClazz(Delegate eventPacketDelegate)
        {
            Type type = eventPacketDelegate.GetType().GenericTypeArguments[0];
            return type;
        }
         
    }
}
